interface XIF;
    logic tclk;
    logic dclk;
    modport DUT (
        input   tclk,
        output  dclk
    );
    modport TB (
        output  tclk,
        input   dclk
    );
endinterface

module DUT (
    XIF.DUT xif
);
    initial begin
        xif.dclk = 0;
        forever #5 xif.dclk = ~xif.dclk;
    end

    always_ff @(posedge xif.dclk) begin
        $display("@(posedge xif.dclk) %0tns DUT\tdclk=%b, tclk=%b", $time, xif.dclk, xif.tclk);
    end

    always_ff @(posedge xif.tclk) begin
        $display("@(posedge xif.tclk) %0tns DUT\tdclk=%b, tclk=%b", $time, xif.dclk, xif.tclk);
    end

endmodule

program TB (
    XIF.TB xif
);

    initial begin
        xif.tclk = 0;
        forever #5 xif.tclk = ~xif.tclk;
    end

    initial begin
        // 必须加上 forever, 否则此 initial 块只会执行一次
        forever @(posedge xif.tclk) $display("@(posedge xif.tclk) %0tns TB\tdclk=%b, tclk=%b", $time, xif.dclk, xif.tclk);
    end

    initial begin
        // 必须加上 forever, 否则此 initial 块只会执行一次
        forever @(posedge xif.dclk) $display("@(posedge xif.dclk) %0tns TB\tdclk=%b, tclk=%b", $time, xif.dclk, xif.tclk);
    end

    initial begin
        #51 $finish;
    end

endprogram

module test_prog_top;
    XIF xif();

    DUT dut_inst (.xif(xif.DUT));
    TB  tb_inst  (.xif(xif.TB));

endmodule

/** 仿真结果分析:
TB(Program Block) 的 tclk 总是在当前时间槽的末尾区域被驱动, 而 DUT 的 dclk 则是在当前时间槽的开始区域被驱动。
*/

/* Output in VCS:
@(posedge xif.dclk) 5ns DUT	dclk=1, tclk=0
@(posedge xif.dclk) 5ns TB	dclk=1, tclk=1
@(posedge xif.tclk) 5ns DUT	dclk=1, tclk=1
@(posedge xif.tclk) 5ns TB	dclk=1, tclk=1
*/

/* Output in Vivado:
@(posedge xif.dclk) 5000ns DUT  dclk=1, tclk=0
@(posedge xif.dclk) 5000ns TB   dclk=1, tclk=0
@(posedge xif.tclk) 5000ns TB   dclk=1, tclk=1
@(posedge xif.tclk) 5000ns DUT  dclk=1, tclk=1
*/

/* Output in QuestaSim:
# @(posedge xif.dclk) 5ns DUT	dclk=1, tclk=0
# @(posedge xif.dclk) 5ns TB	dclk=1, tclk=0
# @(posedge xif.tclk) 5ns TB	dclk=1, tclk=1
# @(posedge xif.tclk) 5ns DUT	dclk=1, tclk=1
*/
